Skip to content

Sediakan pengelolaan QR Code pengganti TTE#1580

Open
lukman48 wants to merge 7 commits into
OpenSID:rilis-devfrom
lukman48:fitur/qr-code-tte
Open

Sediakan pengelolaan QR Code pengganti TTE#1580
lukman48 wants to merge 7 commits into
OpenSID:rilis-devfrom
lukman48:fitur/qr-code-tte

Conversation

@lukman48

Copy link
Copy Markdown

Deskripsi

Menyediakan fitur pengelolaan QR Code sebagai pengganti TTE (Tanda Tangan Elektronik) dari BSRE. Fitur ini memungkinkan camat untuk menandatangani surat dengan QR Code yang dihasilkan sendiri oleh OpenDK tanpa bergantung pada layanan eksternal BSRE.

Masalah Terkait

Close #219

Perubahan yang dilakukan

Library Baru

  • endroid/qr-code (^5.0): Untuk menghasilkan QR Code di sisi server
  • setasign/fpdi (^2.3): Untuk menyematkan QR Code ke dalam PDF yang sudah ada

Migration

  • : Menambahkan kolom file_hash (SHA-256) ke tabel das_log_surat

Model

  • Surat: Menambahkan file_hash ke $fillable

Controller

  • PermohonanController: Menambahkan method tandatanganQr() yang:

    • Hanya dapat diakses oleh pengguna camat
    • Menghasilkan QR Code berisi tautan verifikasi
    • Menyematkan QR Code di halaman terakhir surat PDF
    • Menyimpan hash SHA-256 file yang sudah ditandatangani
    • Mengubah status surat menjadi arsip
  • SuratController: Menambahkan method:

    • verifikasi(): Menampilkan halaman unggah file untuk verifikasi
    • verifikasiStore(): Memproses unggahan, mencocokkan hash dengan database
    • Menambahkan kolom hash pada DataTable arsip

Routes

  • POST surat/permohonan/tandatangan-qr/{surat}: Endpoint tanda tangan QR Code
  • GET surat/verifikasi: Halaman verifikasi surat
  • POST surat/verifikasi: Proses verifikasi unggahan file

Views

  • show.blade.php: Menambahkan tombol "Tandatangani dengan QR Code" pada tahap ProsesTTE
  • arsip.blade.php: Menambahkan kolom hash pada tabel arsip
  • qrcode.blade.php: Menampilkan hash file pada halaman verifikasi QR, menyesuaikan branding untuk QR Code mandiri
  • surat/verifikasi/index.blade.php: Halaman unggah file untuk verifikasi (baru)
  • surat/verifikasi/hasil.blade.php: Halaman hasil verifikasi (baru)
  • sidebar.blade.php: Menambahkan menu Verifikasi pada grup Layanan Surat

Alur Kerja

  1. Surat masuk sebagai permohonan → diverifikasi operator/sekretaris/camat
  2. Setelah camat menyetujui, surat masuk tahap ProsesTTE
  3. Camat klik "Tandatangani dengan QR Code"
  4. Sistem menghasilkan QR Code berisi tautan verifikasi, menyematkannya di PDF, menyimpan hash
  5. Surat masuk ke arsip (SudahTTE)
  6. Penerima surat dapat memverifikasi keaslian file dengan mengunggah PDF di halaman Verifikasi

Daftar Periksa

  • Saya telah mematuhi aturan penulisan script.
  • Saya telah mengikuti proses review pull request.

- Generate QR Code per surat menggunakan endroid/qr-code
- Sematkan QR Code di halaman terakhir surat menggunakan FPDI
- QR Code berisi tautan ke halaman verifikasi surat
- Simpan hash SHA-256 dari isi file untuk verifikasi keaslian
- Sediakan fitur verifikasi file unggahan (unggah PDF, sistem cocokkan hash)
- Penyematan QR Code hanya bisa dilakukan oleh pengguna camat
- Tampilkan kolom hash di arsip surat dan halaman verifikasi

Closes OpenSID#219

@pandigresik pandigresik left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mas @lukman48 fix conflict dulu, untuk branch tujuan gunakan rilis-dev bukan master

@pandigresik
pandigresik changed the base branch from master to rilis-dev July 15, 2026 22:35
@pandigresik

Copy link
Copy Markdown
Contributor

Code Review: fitur/qr-code-tte vs rilis-dev

Branch: fitur/qr-code-tte compared to rilis-dev
Commits: 3 (fix conflict, Merge remote-tracking branch, feat: sediakan pengelolaan QR Code pengganti TTE)
Files Changed: 13 (+615, -12)

Summary

Menambahkan fitur penandatanganan surat menggunakan QR Code sebagai alternatif TTE (Tanda Tangan Elektronik) BSrE, termasuk penghitungan hash SHA-256 untuk verifikasi keaslian file, halaman verifikasi surat, dan integrasi QR Code pada PDF surat.

Verdict: [x] Request Changes | [ ] Approve | [ ] Comment


Critical Issues (Must Fix)

1. PermohonanController.php:311-363 — Race condition pada operasi file bisa mengakibatkan kehilangan file surat

Metode tandatanganQr melakukan operasi file secara berurutan: @unlink($file_path) lalu rename($signed_path, $file_path). Jika rename gagal (misal karena izin filesystem), file original sudah dihapus tetapi file signed tidak dipindahkan ke path yang benar. Akibatnya database merujuk ke file yang tidak ada di disk.

Meskipun rename di Linux umumnya atomic dan akan menimpa destination, penggunaan @unlink yang menekan error membuat kegagalan cleanup tidak terdeteksi. Temp file qr_temp_*.png bisa menumpuk di disk jika cleanup gagal.

// Current — jika rename gagal, file original sudah hilang
@unlink($qrTempPath);
$fileHash = hash_file('sha256', $signed_path);
@unlink($file_path);
rename($signed_path, $file_path);

// Suggested — gunakan Storage::move atau pastikan rename berhasil sebelum unlink
$fileHash = hash_file('sha256', $signed_path);
if (!rename($signed_path, $file_path)) {
    throw new \RuntimeException('Gagal memindahkan file signed');
}
@unlink($qrTempPath);

Impact: Kehilangan file surat secara permanen jika rename gagal.


Major Issues (Should Fix)

1. PermohonanController.php:298-385 — Tidak ada pencatatan audit LogTte untuk penandatangan QR Code

Metode passphrase menggunakan $this->response() yang mencatat ke LogTte::create(). Metode tandatanganQr yang baru tidak mencatat ke LogTte sama sekali. Penandatanganan QR Code tidak memiliki jejak audit.

// Current — tidak ada LogTte
$surat->update([...]);
DB::commit();
return response()->json([...]);

// Suggested — tambahkan pencatatan seperti passphrase
LogTte::create([
    'pesan_error' => 'success',
    'jenis' => 'QRCode',
]);

Impact: Penandatanganan QR Code tidak tercatat di log audit. Sulit melakukan investigasi atau pelacakan jika ada masalah di kemudian hari.

2. PermohonanController.php:330 — Temp file QR code bisa terjadi race condition

Nama file temporary qr_temp_{$surat->id}.png menggunakan ID surat. Jika dua request penandatanganan untuk surat yang sama terjadi secara konkuren (meskipun kecil kemungkinan), file temp akan bertabrakan.

// Current — collision risk
$qrTempPath = public_path('storage/surat/qr_temp_' . $surat->id . '.png');

// Suggested — gunakan nama unik
$qrTempPath = public_path('storage/surat/qr_temp_' . uniqid() . '_' . $surat->id . '.png');

Impact: Kemungkinan kecil tapi nyata — QR code dari request lain tertimpa.

3. PermohonanController.php:351,355 — Error suppression pada @unlink menyembunyikan kegagalan cleanup

Penggunaan @ pada unlink menyembunyikan semua error, termasuk error izin atau file tidak ditemukan. Jika file temp tidak bisa dihapus, tidak ada log atau notifikasi.

Impact: File temp menumpuk di disk tanpa jejak error di log aplikasi.

4. SuratController.php:83-88 — Kolom hash di DataTables menampilkan raw HTML tanpa escaping

Kolom hash ditambahkan ke rawColumns dan menampilkan string dari substr() tanpa HTML escaping. Meskipun hash hanya berisi karakter hex, penggunaan rawColumns adalah pola yang berisiko jika kolom lain ditambahkan tanpa perhatian.

// Current — raw HTML
return '<code style="font-size: 10px;">' . substr($row->file_hash, 0, 16) . '...</code>';

// Suggested — lebih aman dengan e()
return '<code style="font-size: 10px;">' . e(substr($row->file_hash, 0, 16)) . '...</code>';

Impact: Risiko XSS rendah untuk data hash, tapi menjadi kebiasaan yang berisiko jika diterapkan ke kolom lain.


Minor Issues (Nice to Have)

1. show.blade.php:41 — Magic number 4 untuk status verifikasi

@if ($surat->log_verifikasi == 4)

Seharusnya menggunakan enum LogVerifikasiSurat::ProsesTTE seperti yang digunakan di controller.

2. verifikasi/index.blade.php:28$profil->nama_kecamatan bisa null

Meskipun $profil di-share secara global dari base controller, fallback ?? '' sudah ditambahkan. Namun, jika $profil itu sendiri null, ini akan throw error. Pertimbangkan optional($profil)->nama_kecamatan ?? ''.

3. qrcode.blade.php:74 — Perubahan fallback penduduk->nama tanpa null check pada penduduk

// Current
<?= 'a/n ' . ($surat->penduduk->nama ?? $surat->nama_penduduk) ?>

Jika $surat->penduduk adalah null (relasi tidak ditemukan), ini akan throw error. Pola yang lebih aman:

<?= 'a/n ' . ($surat->penduduk->nama ?? $surat->nama_penduduk ?? '-') ?>

Note: Ini juga diterapkan di hasil.blade.php:48 dengan pola yang sama.


Positive Feedback

  • Fitur QR Code sebagai alternatif TTE BSrE adalah solusi yang baik untuk kecamatan yang belum terintegrasi BSrE
  • Penggunaan SHA-256 hash untuk verifikasi keaslian file sudah tepat
  • Error handling dengan DB::beginTransaction/commit/rollback sudah benar
  • Logging error dengan konteks yang cukup lengkap (user_id, surat_id)
  • Integrasi FPDI untuk overlay QR code pada PDF dilakukan dengan benar
  • UI konfirmasi SweetAlert2 konsisten dengan pola yang sudah ada

Questions for Author

  1. Verifikasi publik vs internal — Halaman verifikasi (surat.verifikasi) berada di dalam middleware access.surat. Apakah ini disengaja? Jika QR code ditujukan untuk verifikasi oleh pihak eksternal, halaman ini perlu diakses tanpa login.
  2. Pencatatan LogTte — Apakah ada alasan mengapa penandatanganan QR Code tidak dicatat ke LogTte seperti penandatanganan TTE?
  3. Race condition file — Apakah ada mekanisme di aplikasi yang mencegah dua user menandatangani surat yang sama secara bersamaan?

Checklist

  • Tidak ada vulnerability keamanan (SQL injection, XSS)
  • Error handling pada controller sudah benar
  • Race condition pada operasi file perlu diperbaiki
  • Pencatatan audit LogTte belum dilakukan untuk QR signing
  • Migration sudah benar (nullable, length 64 untuk SHA-256)
  • View konsisten dengan pola yang ada
  • Route dan middleware sudah sesuai

@pandigresik pandigresik left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lihat komentar sebelumnya

- Fix race condition: rename file signed sebelum hapus file asal
- Tambahkan LogTte audit logging untuk penandatangan QR Code
- Fix temp file naming collision dengan uniqid()
- Hapus @Unlink error suppression, gunakan file_exists check
- Escape HTML di DataTable hash column dengan e()
- Gunakan enum LogVerifikasiSurat::ProsesTTE bukan magic number 4
- Null safety untuk $profil dan $surat->penduduk
- Pindahkan route verifikasi ke publik (tanpa login)
@lukman48

Copy link
Copy Markdown
Author

Revisi Code Review

Terima kasih atas reviewnya yang sangat detail @pandigresik. Berikut respon terhadap semua issue yang telah diperbaiki:


Critical Issues

1. Race condition pada operasi file (PermohonanController.php:311-363)

Sudah diperbaiki. Urutan operasi file diubah:

  • rename($signed_path, $file_path) dilakukan sebelum menghapus file asal
  • Jika rename gagal, file asal tetap aman dan exception di-throw
  • @unlink diganti dengan file_exists() + unlink() untuk temp file cleanup

Major Issues

1. Tidak ada pencatatan audit LogTte (PermohonanController.php:298-385)

Sudah ditambahkan. Sekarang LogTte::create() dipanggil setelah penandatanganan QR Code berhasil dengan nilai jenis => QRCode.

2. Temp file naming collision (PermohonanController.php:330)

Sudah diperbaiki. Nama file temporary menggunakan uniqid() untuk mencegah tabrakan jika ada request konkuren:

$qrTempPath = public_path('storage/surat/qr_temp_' . uniqid() . '_' . $surat->id . '.png');

3. Error suppression pada @unlink (PermohonanController.php:351,355)

Sudah diperbaiki. @unlink diganti dengan pengecekan file_exists() sebelum unlink().

4. Raw HTML tanpa escaping di DataTable (SuratController.php:83-88)

Sudah ditambahkan e() untuk escaping output:

return '<code style="font-size: 10px;">' . e(substr($row->file_hash, 0, 16)) . '...</code>';

Minor Issues

1. Magic number 4 (show.blade.php:41)

Sudah diganti dengan enum App\Enums\LogVerifikasiSurat::ProsesTTE.

2. $profil->nama_kecamatan bisa null (verifikasi/index.blade.php:28)

Sudah diganti dengan optional($profil)->nama_kecamatan ?? ''.

3. $surat->penduduk->nama tanpa null check (qrcode.blade.php:74 & hasil.blade.php:48)

Sudah ditambahkan fallback ?? - di kedua file.


Jawaban untuk Pertanyaan

Q1: Verifikasi publik vs internal — Halaman verifikasi berada di dalam middleware access.surat. Apakah ini disengaja?

Tidak disengaja. Sudah diperbaiki dengan memindahkan route verifikasi ke luar middleware auth:web, sehingga halaman verifikasi bisa diakses publik tanpa login.

Q2: Pencatatan LogTte — Apakah ada alasan mengapa penandatanganan QR Code tidak dicatat ke LogTte seperti penandatanganan TTE?

Tidak ada alasan khusus, ini memang terlewat. Sekarang sudah ditambahkan pencatatan LogTte untuk QR signing.

Q3: Race condition file — Apakah ada mekanisme di aplikasi yang mencegah dua user menandatangani surat yang sama secara bersamaan?

Tidak ada mekanisme eksplisit. Namun dengan perbaikan rename() sebelum unlink() dan penambahan uniqid() pada nama temp file, risiko kehilangan file sudah diminimalkan.


Files Changed

File Perubahan
PermohonanController.php Fix race condition, tambah LogTte, fix temp file naming, hapus @Unlink
SuratController.php Tambah e() untuk escaping HTML
show.blade.php Ganti magic number 4 dengan enum
qrcode.blade.php Tambah null fallback ?? -
hasil.blade.php Tambah null fallback ?? -
verifikasi/index.blade.php Pakai optional() untuk null safety
routes/web.php Pindahkan route verifikasi ke publik

@apidong

apidong commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

🔐 Security Audit — PR #1580 (QR Code pengganti TTE)

Auditor: Senior Server Security Auditor — fokus OpenSID Premium (Ubuntu LTS, PHP 7.4–8.3)

✅ Kesimpulan: APPROVE WITH MINOR CHANGES

Secara arsitektur implementasi ini aman dan mengikuti pola otorisasi yang sudah ada. Tidak ditemukan webshell/malware/injection. Namun ada 1 isu HIGH dan 2 MEDIUM yang sebaiknya diperbaiki sebelum merge.


📊 Ringkasan Temuan

# Severity Lokasi Masalah
1 🟠 HIGH verifikasi/hasil.blade.php + route publik Kebocoran PII — halaman verifikasi publik menampilkan nama penduduk, desa, nomor surat
2 🟡 MEDIUM PermohonanController::tandatanganQr() Path traversal$surat->file langsung ke public_path() tanpa basename()
3 🟡 MEDIUM POST /surat/verifikasi Tidak ada rate-limit — endpoint publik bisa di-abuse (DoS ringan / brute-force)
4 🔵 LOW tandatanganQr() catch block Error leak$e->getMessage() dikirim ke response JSON
5 🔵 LOW composer.json Dependency baru (endroid/qr-code ^6.1, setasign/fpdi ^2.6) — legit, perlu cek versi

🟠 HIGH-1 — Kebocoran PII pada Verifikasi Publik

Route surat/verifikasi sengaja publik (tanpa login) — wajar untuk verifikasi eksternal. Tapi hasil.blade.php menampilkan:

  • Nama penduduk (atas nama)
  • Nama desa
  • Nomor & tanggal surat

Siapa pun yang punya file PDF asli bisa upload & dapatkan data pribadi warga tanpa autentikasi → melanggar prinsip perlindungan data (UU PDP).

Fix: Halaman verifikasi publik hanya boleh menampilkan status TERVERIFIKASI / TIDAK VALID + keterangan umum (mis. "Surat sah diterbitkan Kecamatan X"), tanpa field PII.


🟡 MEDIUM-1 — Path Traversal

$file_path = public_path("storage/surat/{$surat->file}");
$pdf->setSourceFile($file_path);
rename($signed_path, $file_path);

Nilai $surat->file dari DB disisipkan ke public_path() tanpa sanitasi. Jika DB terkompromi, bisa baca/timpa file di luar storage/surat/.

Fix:

$file_path = public_path('storage/surat/' . basename($surat->file));

🟡 MEDIUM-2 — Throttle Endpoint Publik

Route::post('/surat/verifikasi', [...])->middleware('throttle:10,1');

🔵 LOW-1 — Jangan kirim pesan exception ke client

// ganti
'pesan_error' => $e->getMessage(),
// menjadi
'pesan_error' => 'Terjadi kesalahan saat menandatangani surat.',

✅ Aspek yang Sudah Baik

  • ✅ Otorisasi bertingkat: cek ProsesTTE + akun_camat->id + route auth:web + action_permission:access.surat
  • ✅ Race condition sudah diperbaiki (rename dulu, bukan hapus-dulu)
  • ✅ Temp file unik (uniqid() + $surat->id)
  • ✅ HTML escaped (e()) di DataTable hash
  • ✅ Audit log LogTte::create()
  • verifikasiStore hanya hash_file()aman dari file upload abuse (tidak parse/move PDF)

🔗 Relasi dengan Issue #219 (dan #217)

Saran: Jangan auto-close #219 dengan merge ini. Tandai sebagai solusi sementara (interim) & buka issue lanjutan untuk TTE BSRE sesungguhnya. Tambahkan disclaimer di view bahwa ini QR verifikasi internal, bukan TTE bersertifikat BSRE.


Verdict: ✅ Layak merge setelah perbaiki HIGH-1 + MEDIUM-1 (< 30 menit).

CC: @lukman48 @pandigresik

{
public function up()
{
Schema::table('das_log_surat', function (Blueprint $table) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tabel ini belum memiliki reference ke surat tertentu, mungkin bisa ditambahkan column surat_id atau semisalnya

@@ -0,0 +1,70 @@
@extends('layouts.dashboard_template')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

halaman public tapi menggunakan layout admin

@@ -0,0 +1,43 @@
@extends('layouts.dashboard_template')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

halaman public tapi menggunakan layout admin

return view('surat.verifikasi.index', compact('page_title', 'page_description'));
}

public function verifikasiStore(Request $request)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gunakan formRequest seperti pada method lain untuk validasi form

@pandigresik

Copy link
Copy Markdown
Contributor

Code Review: fitur/qr-code-tte vs rilis-dev

Tanggal: 2026-07-17
Branch: fitur/qr-code-tte compared to rilis-dev

Ringkasan

Branch ini menambahkan penandatanganan surat berbasis QR Code sebagai alternatif dari TTE BSrE, serta halaman verifikasi publik untuk memvalidasi keaslian PDF melalui perbandingan hash SHA-256.


Bugs

1. High — File overwrite sebelum DB commit (data integrity risk)

File: app/Http/Controllers/Surat/PermohonanController.php:311-372

Metode tandatanganQr menimpa file PDF asli dengan versi yang sudah di-QR (rename($signed_path, $file_path)) sebelum DB::commit(). Jika update() atau commit() gagal dan transaksi di-rollback, database kembali ke status "belum ditandatangani", tetapi file di disk sudah terganti dengan versi yang sudah ditandatangani. File asli yang belum ditandatangani hilang permanen.

Bandingkan dengan metode passphrase() (line 267-274) yang menulis file setelah DB::commit():

// passphrase() — urutan benar:
DB::commit();
if ($response->getStatusCode() == 200) {
    $file = fopen($file_path, 'wb');
    fwrite($file, $response->getBody()->getContents());
    fclose($file);
}
// tandatanganQr() — urutan salah:
$fileHash = hash_file('sha256', $signed_path);
if (!rename($signed_path, $file_path)) {  // file asli hancur di sini
    throw new \RuntimeException('...');
}
$surat->update([...]);
DB::commit();  // jika gagal, file dan DB tidak sinkron

Fix: Pindahkan rename() ke setelah DB::commit(), sesuai pola passphrase(). Bersihkan file sementara ($signed_path) jika terjadi kegagalan.


2. Medium — Exception message bocor ke client

File: app/Http/Controllers/Surat/PermohonanController.php:387-391

return response()->json([
    'status' => false,
    'pesan_error' => $e->getMessage(),  // pesan error internal terekspos
    'jenis' => 'Exception',
]);

$e->getMessage() dapat berisi path file system, error PHP internal, atau detail library. Informasi ini tidak boleh dikembalikan ke client. Gunakan pesan error generik, dan simpan detail di Log::error() (line 381).


3. Medium — Halaman verifikasi publik menggunakan layout admin

File: resources/views/surat/verifikasi/index.blade.php:1, resources/views/surat/verifikasi/hasil.blade.php:1

Keduanya extend layouts.dashboard_template yang merender full admin dashboard (sidebar navigation, admin menu, user profile, dll). Route verifikasi ditempatkan di luar middleware auth (routes/web.php:299-303, komentar: "publik - tidak perlu login"), sehingga pengunjung yang tidak login akan melihat layout admin dengan navigasi yang tidak berfungsi. Seharusnya menggunakan layout publik yang lebih sederhana.


4. Medium — File sementara tidak dibersihkan saat rename gagal

File: app/Http/Controllers/Surat/PermohonanController.php:357-358

if (!rename($signed_path, $file_path)) {
    throw new \RuntimeException('Gagal memindahkan file signed ke lokasi asal.');
}

Jika rename() gagal, exception di-catch dan DB di-rollback, tetapi $signed_path ({filename}_signed.pdf) tetap tersisa di disk. Block catch tidak membersihkan file sementara. Hal yang sama berlaku untuk $qrTempPath jika exception terjadi antara pembuatan QR temp file dan penghapusannya di line 351.


5. Medium — tolak() — tidak ada validasi pada keterangan

File: app/Http/Controllers/Surat/PermohonanController.php:209-226

public function tolak(Request $request, $id)
{
    try {
        Surat::findOrFail($id)->update([
            'log_verifikasi' => LogVerifikasiSurat::Ditolak,
            'status' => StatusSurat::Ditolak,
            'keterangan' => $request['keterangan'],  // tanpa validasi
        ]);

$request['keterangan'] ditulis langsung ke database tanpa validasi. Tidak ada pengecekan required, sanitasi, atau max length. Seharusnya menggunakan FormRequest (contoh: TolakSuratRequest) atau inline $request->validate().


6. Medium — passphrase() — tidak ada validasi pada passphrase

File: app/Http/Controllers/Surat/PermohonanController.php:228-296

public function passphrase(Request $request, $id)
{
    // ...
    'passphrase' => 'contents' => $request['passphrase'],  // tanpa validasi

Passphrase dikirim langsung ke API BSrE tanpa validasi. Jika kosong atau tidak ada, HTTP request akan gagal dengan error yang tidak jelas. Seharusnya validasi bahwa passphrase required dan non-empty sebelum melakukan HTTP request.


7. Minor — LogTte::create tidak punya surat_id

File: app/Http/Controllers/Surat/PermohonanController.php:367-370

LogTte::create([
    'pesan_error' => 'success',
    'jenis' => 'QRCode',
]);

Tidak ada cara mengaitkan record LogTte dengan surat tertentu. Pertimbangkan untuk menambahkan surat_id sebagai foreign key untuk keperluan audit trail.


8. Minor — Tidak ada return type declaration

File: app/Http/Controllers/Surat/PermohonanController.php

Seluruh metode tidak mendeklarasikan return type. Berdasarkan konvensi yang sudah ada di controller lain (ArtikelController, PengurusController, RoleController):

Metode Return Type yang Diexpected
index() : View
show($id) : View
ditolak() : View
download($id) : BinaryFileResponse
setujui($id) : JsonResponse
tolak(Request $request, $id) : JsonResponse
passphrase(Request $request, $id) : JsonResponse
tandatanganQr($id) : JsonResponse
getData() : JsonResponse
getDataDitolak() : JsonResponse

9. Minor — Tidak ada type hint pada parameter $id

File: app/Http/Controllers/Surat/PermohonanController.php, app/Http/Controllers/Surat/SuratController.php

Route sudah menggunakan nama model sebagai parameter ({surat}), yang memungkinkan Laravel route model binding. Namun semua controller method mendefinisikan $id tanpa type hint:

// Routes (web.php:866-873):
Route::get('show/{surat}', ...);
Route::get('download/{surat}', ...);
Route::get('setujui/{surat}', ...);
Route::post('tolak/{surat}', ...);
Route::post('passphrase/{surat}', ...);
Route::post('tandatangan-qr/{surat}', ...);

// Controller — tanpa type hint:
public function show($id)           // PermohonanController:136
public function download($id)       // PermohonanController:162, SuratController:92
public function setujui($id)        // PermohonanController:179
public function tolak(Request $request, $id)   // PermohonanController:209
public function passphrase(Request $request, $id)  // PermohonanController:228
public function tandatanganQr($id)  // PermohonanController:298
public function qrcode($id)         // SuratController:139

Jika menggunakan route model binding (Surat $surat), Laravel akan:

  1. Auto-resolve model dari route parameter
  2. Auto-return 404 ModelNotFoundException jika tidak ditemukan
  3. Menghilangkan kebutuhan Surat::findOrFail($id) secara manual

Bandingkan dengan pola yang sudah ada di ArtikelController:

public function edit(Artikel $artikel): View
public function update(ArtikelRequest $request, Artikel $artikel): RedirectResponse
public function destroy(Artikel $artikel): RedirectResponse

10. Minor — Type hint $notif pada response() helper

File: app/Http/Controllers/Surat/PermohonanController.php:395

protected function response($notif = [])

Parameter $notif tidak memiliki type hint. Seharusnya array $notif atau lebih spesifik lagi menggunakan type hint array keys jika memungkinkan.


11. Minor — Gaya akses parameter request tidak konsisten

Controller mencampur dua gaya:

  • $request['keterangan'] (array access) — line 215, 253
  • $request->file('file') (method call) — di SuratController

Seharusnya konsisten menggunakan $request->input() atau $request->validated() (dengan FormRequest).


Prioritas Perbaikan

# Severity Issue Effort
1 High File overwrite sebelum DB commit Sedang
2 Medium Exception message bocor ke client Kecil
3 Medium Layout admin pada halaman publik Sedang
4 Medium File sementara tidak dibersihkan Kecil
5 Medium Validasi keterangan di tolak() Kecil
6 Medium Validasi passphrase di passphrase() Kecil
7 Minor LogTte tanpa surat_id Kecil
8 Minor Return type declaration Kecil
9 Minor Tidak ada type hint pada parameter $id Kecil
10 Minor Type hint $notif pada response() Kecil
11 Minor Gaya akses request tidak konsisten Kecil

lukman48 and others added 2 commits July 19, 2026 20:26
- High: pindahkan rename() setelah DB::commit() (data integrity)
- Medium: exception message generik, hapus @Unlink, cleanup temp files
- Medium: validasi keterangan (tolak) dan passphrase
- Medium: rate-limit throttle:10,1 di endpoint publik
- Medium: path traversal - basename() pada ->file
- Medium: layout publik untuk halaman verifikasi
- High: PII tidak ditampilkan di halaman verifikasi publik
- Minor: route model binding, return types, type hints
- Minor: LogTte surat_id (fillable + migration)
- Minor: konsistensi akses request (->input)
</li>
<li {{ Request::is(['surat/arsip*']) ? 'class=active' : '' }}><a href="{{ route('surat.arsip') }}"><i class="fa fa-folder-open"></i>Arsip</a>
</li>
<li {{ Request::is(['surat/verifikasi*']) ? 'class=active' : '' }}><a href="{{ route('surat.verifikasi') }}"><i class="fa fa-qrcode"></i>Verifikasi</a>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apakah ini masih diperlukan ? Karena saya lihat ini route publik bukan route untuk admin

@pandigresik

Copy link
Copy Markdown
Contributor

Mohon lakukan test ulang setelah perbaikan

image

}

public function tolak(Request $request, $id)
public function tolak(Request $request, Surat $surat): JsonResponse

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

masih belum menggunakan formRequest untuk validasi

}

public function passphrase(Request $request, $id)
public function passphrase(Request $request, Surat $surat): JsonResponse

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gunakan formRequest untuk validasi

}
}

public function tandatanganQr(Surat $surat): JsonResponse

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tambahkan testing untuk method baru, lengkapi pada tests/Feature/PermohonanControllerTest.php

@@ -37,21 +37,26 @@
use App\Models\Profil;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buatkan unit/feature test untuk controller baru

return view('surat.qrcode', compact('surat', 'profil'));
}

public function verifikasi(): View

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buatkan unit/feature test untuk method baru

@pandigresik

Copy link
Copy Markdown
Contributor

Tambahkan juga video yang menunjukkan bahwa fitur surat dengan QR Code ini berhasil dijalankan pada aplikasi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sediakan pengelolaan QR Code pengganti TTE

4 participants